<template>
  <transition
    :name="animationName"
    :mode="mode"
    :appear="appear"
    @before-enter="beforeEnter"
    @enter="enter"
    @after-enter="afterEnter"
    @before-leave="beforeLeave"
    @leave="leave"
    @after-leave="afterLeave"
  >
    <div
      v-if="visible"
      :class="componentClasses"
      :style="componentStyles"
      @animationend="onAnimationEnd"
    >
      <slot />
    </div>
  </transition>
</template>

<script setup lang="ts">
import { computed, ref, watch } from 'vue'

/**
 * 通用动画组件
 * @description 提供各种动画效果，支持自定义动画参数
 */

interface Props {
  // 动画类型
  type?: 'fade' | 'slide' | 'scale' | 'bounce' | 'rotate' | 'stagger'
  // 动画方向 (仅对slide类型有效)
  direction?: 'up' | 'down' | 'left' | 'right'
  // 动画时长
  duration?: 'fast' | 'normal' | 'slow'
  // 动画延迟
  delay?: number
  // 是否显示
  visible?: boolean
  // 过渡模式
  mode?: 'in-out' | 'out-in'
  // 是否在首次渲染时显示动画
  appear?: boolean
  // 自定义类名
  customClass?: string
  // 是否循环动画
  loop?: boolean
  // 循环间隔
  loopInterval?: number
}

const props = withDefaults(defineProps<Props>(), {
  type: 'fade',
  direction: 'up',
  duration: 'normal',
  delay: 0,
  visible: true,
  mode: 'in-out',
  appear: false,
  customClass: '',
  loop: false,
  loopInterval: 3000
})

const emit = defineEmits<{
  'animation-start': [type: string]
  'animation-end': [type: string]
  'animation-loop': [count: number]
}>()

// 动画状态
const isAnimating = ref(false)
const loopCount = ref(0)
let loopTimer: number | null = null

// 计算动画名称
const animationName = computed(() => {
  if (props.type === 'slide') {
    return `slide-${props.direction}`
  }
  return props.type
})

// 计算组件类名
const componentClasses = computed(() => {
  const classes = ['animated-component']
  
  if (props.customClass) {
    classes.push(props.customClass)
  }
  
  if (props.type === 'stagger') {
    classes.push('stagger-item')
  }
  
  return classes.join(' ')
})

// 计算组件样式
const componentStyles = computed(() => {
  const styles: Record<string, string> = {}
  
  if (props.delay > 0) {
    styles['animation-delay'] = `${props.delay}ms`
  }
  
  return styles
})

// 动画钩子函数
const beforeEnter = (el: Element) => {
  isAnimating.value = true
  emit('animation-start', props.type)
}

const enter = (el: Element, done: () => void) => {
  // 根据动画类型设置初始状态
  switch (props.type) {
    case 'fade':
      (el as HTMLElement).style.opacity = '0'
      break
    case 'slide':
      const distance = props.direction === 'up' || props.direction === 'down' ? '20px' : '20px'
      const transform = props.direction === 'up' ? `translateY(${distance})` :
                       props.direction === 'down' ? `translateY(-${distance})` :
                       props.direction === 'left' ? `translateX(${distance})` :
                       `translateX(-${distance})`
      ;(el as HTMLElement).style.transform = transform
      ;(el as HTMLElement).style.opacity = '0'
      break
    case 'scale':
      (el as HTMLElement).style.transform = 'scale(0.9)'
      ;(el as HTMLElement).style.opacity = '0'
      break
    case 'bounce':
      (el as HTMLElement).style.transform = 'scale(0.8)'
      ;(el as HTMLElement).style.opacity = '0'
      break
    case 'rotate':
      (el as HTMLElement).style.transform = 'rotate(-180deg) scale(0.9)'
      ;(el as HTMLElement).style.opacity = '0'
      break
  }
  
  // 触发重排以应用初始状态
  ;(el as HTMLElement).offsetHeight
  
  // 设置目标状态
  requestAnimationFrame(() => {
    (el as HTMLElement).style.transition = `all ${getDuration()}ms ease-out`
    ;(el as HTMLElement).style.opacity = '1'
    ;(el as HTMLElement).style.transform = 'none'
  })
  
  done()
}

const afterEnter = (el: Element) => {
  isAnimating.value = false
  emit('animation-end', props.type)
  
  // 清理样式
  ;(el as HTMLElement).style.transition = ''
  
  // 如果启用循环动画
  if (props.loop) {
    startLoop()
  }
}

const beforeLeave = (el: Element) => {
  isAnimating.value = true
  emit('animation-start', `${props.type}-leave`)
}

const leave = (el: Element, done: () => void) => {
  // 设置离开动画
  ;(el as HTMLElement).style.transition = `all ${getDuration()}ms ease-in`
  ;(el as HTMLElement).style.opacity = '0'
  
  switch (props.type) {
    case 'slide':
      const distance = props.direction === 'up' || props.direction === 'down' ? '20px' : '20px'
      const transform = props.direction === 'up' ? `translateY(-${distance})` :
                       props.direction === 'down' ? `translateY(${distance})` :
                       props.direction === 'left' ? `translateX(-${distance})` :
                       `translateX(${distance})`
      ;(el as HTMLElement).style.transform = transform
      break
    case 'scale':
    case 'bounce':
      ;(el as HTMLElement).style.transform = 'scale(0.9)'
      break
    case 'rotate':
      ;(el as HTMLElement).style.transform = 'rotate(180deg) scale(0.9)'
      break
  }
  
  done()
}

const afterLeave = (el: Element) => {
  isAnimating.value = false
  emit('animation-end', `${props.type}-leave`)
  
  // 清理样式
  ;(el as HTMLElement).style.transition = ''
}

// 获取动画时长
const getDuration = () => {
  switch (props.duration) {
    case 'fast': return 150
    case 'slow': return 500
    default: return 300
  }
}

// 动画结束回调
const onAnimationEnd = () => {
  if (props.loop) {
    loopCount.value++
    emit('animation-loop', loopCount.value)
  }
}

// 开始循环动画
const startLoop = () => {
  if (loopTimer) {
    clearTimeout(loopTimer)
  }
  
  loopTimer = window.setTimeout(() => {
    if (props.visible && props.loop) {
      // 重新触发动画
      const el = document.querySelector('.animated-component')
             if (el) {
         el.classList.remove('animated')
         void (el as HTMLElement).offsetHeight // 触发重排
         el.classList.add('animated')
       }
      startLoop()
    }
  }, props.loopInterval)
}

// 停止循环动画
const stopLoop = () => {
  if (loopTimer) {
    clearTimeout(loopTimer)
    loopTimer = null
  }
}

// 监听visible变化
watch(() => props.visible, (newVisible) => {
  if (!newVisible) {
    stopLoop()
  } else if (props.loop) {
    startLoop()
  }
})

// 组件卸载时清理
import { onUnmounted } from 'vue'
onUnmounted(() => {
  stopLoop()
})
</script>

<style scoped lang="scss">
@use '../assets/styles/components/_animations' as *;
</style> 